Skip to content

[libc][math] Implement nexttoward functions #72763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 21, 2023
Merged

Conversation

nishantwrp
Copy link
Contributor

@nishantwrp nishantwrp commented Nov 18, 2023

Implements the nexttoward, nexttowardf and nexttowardl functions. Also, raise excepts required by the standard in nextafter functions.

cc: @lntue

@llvmbot llvmbot added the libc label Nov 18, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 18, 2023

@llvm/pr-subscribers-libc

Author: Nishant Mittal (nishantwrp)

Changes

Implements the nexttoward, nexttowardf and nexttowardl functions. Also, raise excepts required by the standard in nextafter functions as well.

cc: @lntue


Patch is 27.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/72763.diff

23 Files Affected:

  • (modified) libc/config/darwin/arm/entrypoints.txt (+3)
  • (modified) libc/config/darwin/x86_64/entrypoints.txt (+3)
  • (modified) libc/config/gpu/entrypoints.txt (+2)
  • (modified) libc/config/linux/aarch64/entrypoints.txt (+3)
  • (modified) libc/config/linux/riscv/entrypoints.txt (+3)
  • (modified) libc/config/linux/x86_64/entrypoints.txt (+3)
  • (modified) libc/config/windows/entrypoints.txt (+3)
  • (modified) libc/spec/stdc.td (+4)
  • (modified) libc/src/__support/FPUtil/ManipulationFunctions.h (+44-1)
  • (modified) libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h (+9-1)
  • (modified) libc/src/math/CMakeLists.txt (+4)
  • (modified) libc/src/math/generic/CMakeLists.txt (+36)
  • (added) libc/src/math/generic/nexttoward.cpp (+19)
  • (added) libc/src/math/generic/nexttowardf.cpp (+19)
  • (added) libc/src/math/generic/nexttowardl.cpp (+22)
  • (added) libc/src/math/nexttoward.h (+18)
  • (added) libc/src/math/nexttowardf.h (+18)
  • (added) libc/src/math/nexttowardl.h (+18)
  • (modified) libc/test/src/math/smoke/CMakeLists.txt (+48)
  • (added) libc/test/src/math/smoke/NextTowardTest.h (+223)
  • (added) libc/test/src/math/smoke/nexttoward_test.cpp (+13)
  • (added) libc/test/src/math/smoke/nexttowardf_test.cpp (+13)
  • (added) libc/test/src/math/smoke/nexttowardl_test.cpp (+13)
diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt
index 362138c92a6869f..da4a2345f389c49 100644
--- a/libc/config/darwin/arm/entrypoints.txt
+++ b/libc/config/darwin/arm/entrypoints.txt
@@ -199,6 +199,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt
index 57d21a2ce273574..e29e75ec92dadcd 100644
--- a/libc/config/darwin/x86_64/entrypoints.txt
+++ b/libc/config/darwin/x86_64/entrypoints.txt
@@ -178,6 +178,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     #libc.src.math.nextafter
     #libc.src.math.nextafterf
     #libc.src.math.nextafterl
+    #libc.src.math.nexttoward
+    #libc.src.math.nexttowardf
+    #libc.src.math.nexttowardl
     #libc.src.math.remainderf
     #libc.src.math.remainder
     #libc.src.math.remainderl
diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index 5dda6aa71d36f81..ba86e31ee0adcd1 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -219,6 +219,8 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nearbyintf
     libc.src.math.nextafter
     libc.src.math.nextafterf
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
     libc.src.math.pow
     libc.src.math.powf
     libc.src.math.remainder
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 096e2afb06e5a9d..284feb7b99096ec 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -316,6 +316,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 7dadbc7f8285477..a5f0c91e32d0810 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -325,6 +325,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 83bc8461999ed66..63aa7473115a08e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -329,6 +329,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt
index 5611b399e8bca8b..4c0a6ec37fe4cc2 100644
--- a/libc/config/windows/entrypoints.txt
+++ b/libc/config/windows/entrypoints.txt
@@ -198,6 +198,9 @@ set(TARGET_LIBM_ENTRYPOINTS
     libc.src.math.nextafter
     libc.src.math.nextafterf
     libc.src.math.nextafterl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
     libc.src.math.powf
     libc.src.math.remainderf
     libc.src.math.remainder
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 4e38cb742d2844e..58c95b856535a12 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -492,6 +492,10 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"nextafter", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
           FunctionSpec<"nextafterl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
 
+          FunctionSpec<"nexttowardf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<LongDoubleType>]>,
+          FunctionSpec<"nexttoward", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<LongDoubleType>]>,
+          FunctionSpec<"nexttowardl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
+
           FunctionSpec<"powf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
           FunctionSpec<"pow", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
 
diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h
index 6d62a2d0fcdf2be..ff131195528ddb0 100644
--- a/libc/src/__support/FPUtil/ManipulationFunctions.h
+++ b/libc/src/__support/FPUtil/ManipulationFunctions.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_MANIPULATIONFUNCTIONS_H
 
 #include "FPBits.h"
+#include "FloatProperties.h"
 #include "NearestIntegerOperations.h"
 #include "NormalFloat.h"
 #include "PlatformDefs.h"
@@ -19,6 +20,7 @@
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
 
+#include <fenv.h>
 #include <limits.h>
 #include <math.h>
 
@@ -169,8 +171,49 @@ LIBC_INLINE T nextafter(T from, T to) {
     int_val = (to_bits.uintval() & sign_mask) + UIntType(1);
   }
 
+  UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
+  if (exponent_bits == UIntType(0))
+    raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+  else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
+    raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+
+  return cpp::bit_cast<T>(int_val);
+}
+
+template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE T nexttoward(T from, long double to) {
+  FPBits<T> from_bits(from);
+  if (from_bits.is_nan())
+    return from;
+
+  FPBits<long double> to_bits(to);
+  if (to_bits.is_nan())
+    return to;
+
+  if ((long double)from == to)
+    return to;
+
+  using UIntType = typename FPBits<T>::UIntType;
+  UIntType int_val = from_bits.uintval();
+  if (from != T(0.0)) {
+    if ((from < to) == (from > T(0.0))) {
+      ++int_val;
+    } else {
+      --int_val;
+    }
+  } else {
+    int_val = FPBits<T>::MIN_SUBNORMAL;
+    if (to_bits.get_sign())
+      int_val |= FloatProperties<T>::SIGN_MASK;
+  }
+
+  UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
+  if (exponent_bits == UIntType(0))
+    raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+  else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
+    raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
+
   return cpp::bit_cast<T>(int_val);
-  // TODO: Raise floating point exceptions as required by the standard.
 }
 
 } // namespace fputil
diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
index 52ef2568c1dce06..354739bab1b1998 100644
--- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
+++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
@@ -59,6 +59,8 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         // which is what is expected. Since NaNs are handling separately,
         // it will never overflow "beyond" infinity.
         from_bits.set_unbiased_exponent(from_bits.get_unbiased_exponent() + 1);
+        if (from_bits.is_inf())
+          raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
         return from_bits;
       } else {
         ++int_val;
@@ -105,6 +107,8 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         // which is what is expected. Since NaNs are handling separately,
         // it will never overflow "beyond" infinity.
         from_bits.set_unbiased_exponent(from_bits.get_unbiased_exponent() + 1);
+        if (from_bits.is_inf())
+          raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
         return from_bits;
       } else {
         ++int_val;
@@ -112,8 +116,12 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
     }
   }
 
+  UIntType implicit_bit =
+      int_val & (UIntType(1) << MantissaWidth<long double>::VALUE);
+  if (implicit_bit == UIntType(0))
+    raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+
   return cpp::bit_cast<long double>(int_val);
-  // TODO: Raise floating point exceptions as required by the standard.
 }
 
 } // namespace fputil
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 4aea54c7a457547..ffabc27bc00abc7 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -187,6 +187,10 @@ add_math_entrypoint_object(nextafter)
 add_math_entrypoint_object(nextafterf)
 add_math_entrypoint_object(nextafterl)
 
+add_math_entrypoint_object(nexttoward)
+add_math_entrypoint_object(nexttowardf)
+add_math_entrypoint_object(nexttowardl)
+
 add_math_entrypoint_object(pow)
 add_math_entrypoint_object(powf)
 
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index 7ac365f55bb6f83..da59e74a8e822a8 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -1474,6 +1474,42 @@ add_entrypoint_object(
     -O2
 )
 
+add_entrypoint_object(
+  nexttoward
+  SRCS
+    nexttoward.cpp
+  HDRS
+    ../nexttoward.h
+  DEPENDS
+    libc.src.__support.FPUtil.manipulation_functions
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  nexttowardf
+  SRCS
+    nexttowardf.cpp
+  HDRS
+    ../nexttowardf.h
+  DEPENDS
+    libc.src.__support.FPUtil.manipulation_functions
+  COMPILE_OPTIONS
+    -O2
+)
+
+add_entrypoint_object(
+  nexttowardl
+  SRCS
+    nexttowardl.cpp
+  HDRS
+    ../nexttowardl.h
+  DEPENDS
+    libc.src.__support.FPUtil.manipulation_functions
+  COMPILE_OPTIONS
+    -O2
+)
+
 add_entrypoint_object(
   fmod
   SRCS
diff --git a/libc/src/math/generic/nexttoward.cpp b/libc/src/math/generic/nexttoward.cpp
new file mode 100644
index 000000000000000..38b45d6d2f65d9c
--- /dev/null
+++ b/libc/src/math/generic/nexttoward.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of nexttoward function -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/nexttoward.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(double, nexttoward, (double x, long double y)) {
+  return fputil::nexttoward(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/generic/nexttowardf.cpp b/libc/src/math/generic/nexttowardf.cpp
new file mode 100644
index 000000000000000..59a9f805a6946a1
--- /dev/null
+++ b/libc/src/math/generic/nexttowardf.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of nexttowardf function ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/nexttowardf.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(float, nexttowardf, (float x, long double y)) {
+  return fputil::nexttoward(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/generic/nexttowardl.cpp b/libc/src/math/generic/nexttowardl.cpp
new file mode 100644
index 000000000000000..0c887ae0671bc98
--- /dev/null
+++ b/libc/src/math/generic/nexttowardl.cpp
@@ -0,0 +1,22 @@
+//===-- Implementation of nexttowardl function ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/nexttowardl.h"
+#include "src/__support/FPUtil/ManipulationFunctions.h"
+#include "src/__support/common.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(long double, nexttowardl, (long double x, long double y)) {
+  // We can reuse the nextafter implementation because nexttoward behaves
+  // exactly same as nextafter in case of long doubles. Also, we have explcitly
+  // handled the special 80-bit long doubles in nextafter implementation.
+  return fputil::nextafter(x, y);
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/math/nexttoward.h b/libc/src/math/nexttoward.h
new file mode 100644
index 000000000000000..6a5bece9957689c
--- /dev/null
+++ b/libc/src/math/nexttoward.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for nexttoward --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_NEXTTOWARD_H
+#define LLVM_LIBC_SRC_MATH_NEXTTOWARD_H
+
+namespace LIBC_NAMESPACE {
+
+double nexttoward(double x, long double y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARD_H
diff --git a/libc/src/math/nexttowardf.h b/libc/src/math/nexttowardf.h
new file mode 100644
index 000000000000000..7a0eb2a61b4d4b2
--- /dev/null
+++ b/libc/src/math/nexttowardf.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for nexttowardf -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H
+#define LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H
+
+namespace LIBC_NAMESPACE {
+
+float nexttowardf(float x, long double y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H
diff --git a/libc/src/math/nexttowardl.h b/libc/src/math/nexttowardl.h
new file mode 100644
index 000000000000000..be1d8b298185d61
--- /dev/null
+++ b/libc/src/math/nexttowardl.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for nexttowardl -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H
+#define LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H
+
+namespace LIBC_NAMESPACE {
+
+long double nexttowardl(long double x, long double y);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 101bdac995355de..298a1b1a400f1d8 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -1251,6 +1251,54 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+# FIXME: These tests are currently spurious for NVPTX.
+if(NOT LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX)
+  add_fp_unittest(
+    nexttoward_test
+    SUITE
+      libc-math-smoke-tests
+    SRCS
+      nexttoward_test.cpp
+    HDRS
+      NextTowardTest.h
+    DEPENDS
+      libc.include.math
+      libc.src.math.nexttoward
+      libc.src.__support.FPUtil.basic_operations
+      libc.src.__support.FPUtil.fp_bits
+  )
+
+  add_fp_unittest(
+    nexttowardf_test
+    SUITE
+      libc-math-smoke-tests
+    SRCS
+      nexttowardf_test.cpp
+    HDRS
+      NextTowardTest.h
+    DEPENDS
+      libc.include.math
+      libc.src.math.nexttowardf
+      libc.src.__support.FPUtil.basic_operations
+      libc.src.__support.FPUtil.fp_bits
+  )
+endif()
+
+add_fp_unittest(
+  nexttowardl_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    nexttowardl_test.cpp
+  HDRS
+    NextTowardTest.h
+  DEPENDS
+    libc.include.math
+    libc.src.math.nexttowardl
+    libc.src.__support.FPUtil.basic_operations
+    libc.src.__support.FPUtil.fp_bits
+)
+
 # TODO(lntue): The current implementation of fputil::general::fma<float> is only
 # correctly rounded for the default rounding mode round-to-nearest tie-to-even.
 add_fp_unittest(
diff --git a/libc/test/src/math/smoke/NextTowardTest.h b/libc/test/src/math/smoke/NextTowardTest.h
new file mode 100644
index 000000000000000..4d27592a11422bf
--- /dev/null
+++ b/libc/test/src/math/smoke/NextTowardTest.h
@@ -0,0 +1,223 @@
+//===-- Utility class to test different flavors of nexttoward ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H
+#define LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/FPUtil/BasicOperations.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include <fenv.h>
+#include <math.h>
+
+#define ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, expected_exception)      \
+  ASSERT_FP_EQ(result, expected);                                              \
+  ASSERT_FP_EXCEPTION(expected_exception);                                     \
+  LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT)
+
+#define ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected)                          \
+  ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, FE_INEXACT | FE_UNDERFLOW)
+
+#define ASSERT_FP_EQ_WITH_OVERFLOW(result, expected)                           \
+  ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, FE_INEXACT | FE_OVERFLOW)
+
+template <typename T>
+class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::Test {
+  using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
+  using ToFPBits = LIBC_NAMESPACE::fputil::FPBits<long double>;
+  using MantissaWidth = LIBC_NAMESPACE::fputil::MantissaWidth<T>;
+  using UIntType = typename FPBits::UIntType;
+
+  static constexpr int BIT_WIDTH_OF_TYPE =
+      LIBC_NAMESPACE::fputil::FloatProperties<T>::BIT_WIDTH;
+
+  const T zero = T(FPBits::zero());
+  const T neg_zero = T(FPBits::neg_zero());
+  const T inf = T(FPBits::inf());
+  const T neg_inf = T(FPBits::neg_inf());
+  const T nan = T(FPBits::build_quiet_nan(1));
+
+  const long double to_zero = ToFPBits::zero();
+  const long double to_neg_zero = ToFPBits::neg_zero();
+  const long double to_nan = ToFPBits::build_quiet_nan(1);
+
+  const UIntType min_subnormal = FPBits::MIN_SUBNORMAL;
+  const UIntType max_subnormal = FPBits::MAX_SUBNORMAL;
+  const UIntType min_normal = FPBits::MIN_NORMAL;
+  const UIntType max_normal = FPBits::MAX_NORMAL;
+
+public:
+  typedef T (*NextTowardFunc)(T, long double);
+
+  void testNaN(NextTowardFunc func) {
+    ASSERT_FP_EQ(func(nan, 0), nan);
+    ASSERT_FP_EQ(func(0, to_nan), nan);
+  }
+
+  void testBoundaries(NextTowardFunc func) {
+    ASSERT_FP_EQ(func(zero, to_neg_zero), neg_zero);
+    ASSERT_FP_EQ(func(neg_zero, to_zero), zero);
+
+    // 'from' is zero|neg_zero.
+    T x = zero;
+    T result = func(x, 1);
+    UIntType expected_bits = 1;
+    T expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    result = func(x, -1);
+    expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1;
+    expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
+    ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
+
+    x = n...
[truncated]

Comment on lines +174 to +178
UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
if (exponent_bits == UIntType(0))
raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably test the exceptions in the tests for nextafter too. Will do that in a separate pr?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM. Thanks for fixing this TODO item.

}

template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T nexttoward(T from, long double to) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For free function, I personally prefer to use SFINAE on the returning type LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> nexttoward(...), so that it frees the second template parameter to be used if needed.

Copy link
Contributor Author

@nishantwrp nishantwrp Nov 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. just out of curiosity, what is the functional difference b/w the two approaches?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially, when we do SFINAE with template<T, U = enable_if_t<..., void>>, we do not intend to let user use the second template parameter, but nothing prevents user code to be func<T, U>(...), and it might silently get wrong function. If all users do is to call func(...) or func<T> then it doesn't matter.

Copy link
Contributor

@lntue lntue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding these functions! Overall LGTM with a few nits. Do you mind also updating the followings:

Thanks!

@nishantwrp
Copy link
Contributor Author

I've addressed the review comments, please review @lntue

@nishantwrp nishantwrp requested a review from lntue November 21, 2023 09:55
Copy link

github-actions bot commented Nov 21, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@lntue lntue merged commit 0c49fc4 into llvm:main Nov 21, 2023
lntue pushed a commit that referenced this pull request Nov 28, 2023
Follow up to
#72763 (comment).

### Summary
- Add unit tests for raising excepts in `nextafter`. 
- Fixed a bug in testing code for `nexttoward`.  

cc: @lntue

FPBits<long double> to_bits(to);
if (to_bits.is_nan())
return to;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm observing the following warnings when building ninja check-libc with clang:

[286/3491] Building CXX object projects/libc/src/math/gene...es/libc.src.math.generic.nexttowardf.dir/nexttowardf.cpp.o
In file included from /android0/llvm-project/libc/src/math/generic/nexttowardf.cpp:10:
/android0/llvm-project/libc/src/__support/FPUtil/ManipulationFunctions.h:191:12: warning: implicit conversion loses floating-point precision: 'long double' to 'cpp::enable_if_t<cpp::is_floating_point_v<float>, float>' (aka 'float') [-Wimplicit-float-conversion]
    return to;
    ~~~~~~ ^~
/android0/llvm-project/libc/src/math/generic/nexttowardf.cpp:16:18: note: in instantiation of function template specialization '__llvm_libc_18_0_0_git::fputil::nexttoward<float>' requested here
  return fputil::nexttoward(x, y);
                 ^
In file included from /android0/llvm-project/libc/src/math/generic/nexttowardf.cpp:10:
/android0/llvm-project/libc/src/__support/FPUtil/ManipulationFunctions.h:194:12: warning: implicit conversion loses floating-point precision: 'long double' to 'cpp::enable_if_t<cpp::is_floating_point_v<float>, float>' (aka 'float') [-Wimplicit-float-conversion]
    return to;
    ~~~~~~ ^~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That issue should be fixed by #73698. Are you on an updated checkout?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, thanks, slightly behind!

@nishantwrp nishantwrp deleted the nexttoward branch November 30, 2023 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants